package com.urbane.generator.properties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Getter
@Setter
@Component
@ConfigurationProperties(prefix = CodeGeneratorProperties.PREFIX)
public class CodeGeneratorProperties {

    /**
     * 配置属性前缀常量
     * 对应配置文件中的 code-generator 前缀
     */
    public static final String PREFIX = "code-generator";

    /**
     * 是否启用代码生成器（用于前端开关）
     * true：允许生成；false：禁止生成（生产环境建议禁用）
     * 默认值：true
     */
    private boolean enabled = true;

    @NestedConfigurationProperty
    private DataSourceConfig dataSourceConfig;

    @NestedConfigurationProperty
    private GlobalConfig globalConfig;

    @NestedConfigurationProperty
    private PackageConfig packageConfig;

    @NestedConfigurationProperty
    private StrategyConfig strategyConfig;

    @NestedConfigurationProperty
    private InjectionConfig injectionConfig;

    /**
     * 自定义包装类：映射 MyBatis-Plus 的 DataSourceConfig
     * 用于配置数据库连接信息，支持 IDE 智能提示和配置校验
     *
     * @author your-name
     * &#064;date  2024-07-05
     */
    @Getter
    @Setter
    public static class DataSourceConfig {

        /**
         * 数据库 JDBC 连接 URL（必填）
         * 示例：jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
         * 注意：不同数据库格式不同，如 PostgreSQL 使用：jdbc:postgresql://host:port/dbname
         * 默认值：无（必须显式配置）
         */
        private String url;

        /**
         * 数据库用户名（必填）
         * 示例：root、postgres、admin
         * 默认值：无（必须显式配置）
         */
        private String username;

        /**
         * 数据库密码（必填）
         * 建议在生产环境使用密钥管理服务（如 Vault）加密存储
         * 默认值：无（必须显式配置）
         */
        private String password;

        /**
         * JDBC 驱动类全限定名（必填）
         * MySQL：com.mysql.cj.jdbc.Driver
         * PostgreSQL：org.postgresql.Driver
         * Oracle：oracle.jdbc.OracleDriver
         * SQL Server：com.microsoft.sqlserver.jdbc.SQLServerDriver
         * 默认值：com.mysql.cj.jdbc.Driver
         */
        private String driverClassName = "com.mysql.cj.jdbc.Driver";

        /**
         * 数据库类型（用于 MyBatis-Plus 生成器自动识别 SQL 语法）
         * 可选值：MYSQL、POSTGRESQL、ORACLE、SQLSERVER、DB2、H2
         * 注意：必须与 driverClassName 和 URL 保持一致
         * 默认值：MYSQL
         */
        private DbType dbType = DbType.MYSQL;

        /**
         * 数据库 Schema 名称（仅适用于 PostgreSQL、Oracle 等支持 Schema 的数据库）
         * 例如：public、myapp_schema
         * MySQL 无需设置，留空即可
         * 默认值：null
         */
        private String schema;

        /**
         * 数据库目录名称（Catalog，适用于 Oracle、SQL Server）
         * 在 MySQL 中通常为数据库名，与 url 中的 dbname 一致
         * 默认值：null
         */
        private String catalog;

        /**
         * 是否启用 SSL 连接（适用于生产环境加密通信）
         * true：强制使用 SSL；false：禁用 SSL（开发环境常用）
         * 默认值：false
         */
        private Boolean useSSL;

        /**
         * 连接超时时间（毫秒），连接数据库的最大等待时间
         * 超时后抛出异常，避免长时间阻塞
         * 默认值：0（无限等待）
         */
        private Integer connectTimeout;

        /**
         * Socket 读写超时时间（毫秒），网络请求最大等待时间
         * 避免因网络延迟导致生成器卡死
         * 默认值：0（无限等待）
         */
        private Integer socketTimeout;

        @Getter
        public enum DbType {
            MYSQL,
            POSTGRESQL,
            ORACLE,
            SQLSERVER,
            DB2,
            H2
        }
    }

    /**
     * 自定义包装类：映射 MyBatis-Plus 的 GlobalConfig
     * 控制代码生成的全局行为，如作者、输出路径、是否覆盖文件等
     *
     * @author your-name
     * &#064;date  2024-07-05
     */
    @Getter
    @Setter
    public static class GlobalConfig {

        /**
         * 代码生成的作者信息，将写入所有生成文件的注释中
         * 示例：张三、CodeGenBot、公司技术部
         * 默认值：CodeGenWeb
         */
        private String author = "CodeGenWeb";

        /**
         * 是否覆盖已存在的文件（谨慎使用）
         * true：生成时直接覆盖同名文件（可能导致手动修改丢失）
         * false：跳过已存在的文件，保留原有内容（推荐生产环境使用）
         * 默认值：false
         */
        private Boolean fileOverride = false;

        /**
         * 是否开启 XML 缓存（MyBatis-Plus 生成器内部优化）
         * 一般不需要修改，保持默认即可
         * 默认值：false
         */
        private Boolean enableCache = false;

        /**
         * 是否生成 BaseResultMap（XML 中的 <resultMap>）
         * true：生成通用查询结果映射，支持字段别名
         * false：不生成，适用于纯注解模式
         * 默认值：true
         */
        private Boolean baseResultMap = true;

        /**
         * 是否生成 BaseColumnList（XML 中的 <sql id="baseColumnList">）
         * true：生成常用字段列表，方便在 SQL 中复用
         * false：不生成
         * 默认值：true
         */
        private Boolean baseColumnList = true;

        /**
         * 是否开启 ActiveRecord 模式（Entity 继承 Model，支持 save()、update() 等方法）
         * true：生成的 Entity 会继承 Model 类，具备 ORM 操作能力
         * false：Entity 仅为 POJO，需通过 Mapper 操作（推荐）
         * 默认值：false
         */
        private Boolean activeRecord = false;

        /**
         * 生成代码的输出根目录（相对于项目根路径）
         * 通常设置为：./src/main/java（Java 代码）或 ./frontend/src（前端代码）
         * 注意：路径必须存在，否则生成失败
         * 默认值：./src/main/java
         */
        private String outputDir = "./src/main/java";

        /**
         * 生成完成后是否自动打开文件资源管理器（Windows/Mac）
         * true：生成后自动弹出文件夹
         * false：静默生成（推荐用于 CI/CD）
         * 默认值：false
         */
        private Boolean open = false;
    }

    /**
     * 自定义包装类：映射 MyBatis-Plus 的 PackageConfig
     * 定义生成代码的 Java 包路径结构，符合企业规范
     *
     * @author your-name
     * &#064;date  2024-07-05
     */
    @Getter
    @Setter
    public static class PackageConfig {

        /**
         * 所有生成代码的父包名（必须设置）
         * 示例：com.example、com.company.project
         * 默认值：com.example
         */
        private String parent = "com.example";

        /**
         * 代码生成所属的 Maven/Gradle 模块名称（关键！）
         * 用于确定代码输出的**物理目录**，而非仅包名。
         * <p>
         * 适用场景：
         *   - 多模块项目：如 user-service、product-service、common
         *   - 生成路径：./{moduleName}/src/main/java
         *   - 若不设置，则默认输出到主项目根目录下的 ./src/main/java
         * <p>
         * 示例：
         *   moduleName: "user-service" → 输出路径：./user-service/src/main/java
         *   moduleName: "product-api" → 输出路径：./product-api/src/main/java
         * <p>
         * 默认值：null（表示输出到主项目根目录）
         */
        private String moduleName;

        /**
         * 实体类（Entity）所在的包名
         * 生成的 Java 类将位于：${parent}.${entity}
         * 示例：entity、model、pojo
         * 默认值：entity
         */
        private String entity = "entity";

        /**
         * Mapper 接口所在的包名
         * 生成的 Mapper 接口将位于：${parent}.${mapper}
         * 示例：mapper、dao、repository
         * 默认值：mapper
         */
        private String mapper = "mapper";

        /**
         * Service 接口所在的包名
         * 生成的 Service 接口将位于：${parent}.${service}
         * 示例：service、business
         * 默认值：service
         */
        private String service = "service";

        /**
         * Service 实现类所在的包名
         * 生成的 ServiceImpl 类将位于：${parent}.${serviceImpl}
         * 示例：service.impl、service.impl
         * 默认值：service.impl
         */
        private String serviceImpl = "service.impl";

        /**
         * Controller 控制器所在的包名
         * 生成的 Controller 类将位于：${parent}.${controller}
         * 示例：controller、web、api
         * 默认值：controller
         */
        private String controller = "controller";

        /**
         * XML 映射文件（Mapper.xml）所在的包名
         * 生成的 XML 文件将放置在：src/main/resources/${xml} 下
         * 示例：mapper、mybatis/mapper、xml
         * 注意：必须与 packageConfig.mapper 保持一致（通常为 mapper）
         * 默认值：mapper
         */
        private String xml = "mapper";
    }

    /**
     * 自定义包装类：映射 MyBatis-Plus 的 StrategyConfig
     * 控制代码生成的命名策略、字段处理、表过滤等核心规则
     *
     * @author your-name
     * &#064;date  2024-07-05
     */
    @Getter
    @Setter
    public static class StrategyConfig {

        /**
         * 要生成代码的数据库表名列表（支持通配符）
         * 示例：["t_user", "t_product"] 表示只生成这两个表
         * 留空（null）表示生成所有表
         * 注意：表名必须与数据库中一致（区分大小写）
         * 默认值：null（生成所有表）
         */
        private List<String> include;

        /**
         * 数据库表名前缀过滤（自动去除前缀后生成实体类名）
         * 示例：tablePrefix = "t_"，则表 t_user → 实体类 User
         * 如果表名为 user，则无需设置
         * 默认值：null
         */
        private String tablePrefix;

        private Entity entity;

        @Getter
        @Setter
        public static class Entity {
            // 实体类策略
            private Boolean enableFileOverride = false;
            private Boolean disableSerialVersionUID = true;
            private Boolean enableLombok = true;
            private Boolean enableChainModel = false;
            private Boolean enableRemoveIsPrefix = false;
            private Boolean enableTableFieldAnnotation = true;
            private Boolean enableActiveRecord = false;
            private String versionColumnName = "version";
            private String logicDeleteColumnName = "deleted";
            private String superClass = "BaseEntity.class";
            private List<String> addSuperEntityColumns = List.of("id", "created_by", "created_time", "updated_by", "updated_time");
            private List<String> addIgnoreColumns = List.of("age");
            private List<String> addTableFills = List.of("create_time", "update_time"); // 填充字段名，自动映射为 INSERT/INSERT_UPDATE
            private String idType = "AUTO"; // AUTO, INPUT, ASSIGN_ID, ASSIGN_UUID, ID_WORKER, ID_WORKER_STR
            private String formatFileName = "%sEntity";
        }

        private Controller controller;

        @Getter
        @Setter
        public static class Controller {
            private Boolean enableFileOverride = false;
            private Boolean enableRestStyle = true;
            private String formatFileName = "%sController";
        }

        private Service service;

        @Getter
        @Setter
        public static class Service {
            private Boolean enableFileOverride = false;
            private String formatServiceFileName = "%sService";
            private String formatServiceImplFileName = "%sServiceImpl";
        }

        private Mapper mapper;

        @Getter
        @Setter
        public static class Mapper {
            private Boolean enableFileOverride = false;
            private Boolean enableBaseResultMap = true;
            private Boolean enableBaseColumnList = true;
            private Boolean enableMapperAnnotation = true;
            private Boolean enableResultMap = true;
            private Boolean enableTableFieldAnnotation = true;
            private String formatMapperFileName = "%sMapper";
            private String formatXmlFileName = "%sMapper";
        }

        /**
         * 数据库表名到实体类名的命名策略
         * 可选值：underline_to_camel（下划线转驼峰）、no_change（保持原样）
         * 推荐使用：underline_to_camel（符合 Java 命名规范）
         * 默认值：underline_to_camel
         */
        private String naming = "underline_to_camel";

        /**
         * 数据库字段名到实体属性名的命名策略
         * 可选值：underline_to_camel、no_change
         * 推荐与 naming 保持一致
         * 默认值：underline_to_camel
         */
        private String columnNaming = "underline_to_camel";

        /**
         * 是否为实体类自动添加 Lombok 注解（@Data、@Getter、@Setter）
         * true：生成的 Entity 会自动包含 @Data，减少 getter/setter 代码
         * false：不添加，需手动编写
         * 默认值：true
         */
        private Boolean entityLombokModel = true;

        /**
         * 是否为 Controller 添加 @RestController 注解（推荐）
         * true：生成的 Controller 类使用 @RestController
         * false：使用 @Controller（需配合 @ResponseBody）
         * 默认值：true
         */
        private Boolean restControllerStyle = true;

        /**
         * 是否将 Controller 的 URL 路径使用连字符风格（kebab-case）
         * true：/user-info 而不是 /userInfo
         * false：/userInfo（驼峰风格）
         * 推荐：true（符合 RESTful API 规范）
         * 默认值：true
         */
        private Boolean controllerMappingHyphenStyle = true;

        /**
         * 是否为实体类字段添加 @TableField 注解
         * true：即使字段名与数据库列名一致，也生成 @TableField 注解（增强可读性）
         * false：仅在字段名与列名不同时才生成
         * 默认值：true
         */
        private Boolean entityTableFieldAnnotationEnable = true;

        /**
         * 实体类的父类（可选）
         * 用于统一定义公共字段（如 create_time、update_time、deleted）
         * 示例：com.example.base.BaseEntity
         * 默认值：null
         */
        private String superEntityClass;

        /**
         * Mapper 接口的父类（可选）
         * 用于自定义通用方法（如分页、批量操作）
         * 示例：com.baomidou.mybatisplus.core.mapper.BaseMapper
         * 默认值：null（使用 MyBatis-Plus 默认）
         */
        private String superMapperClass;

        /**
         * Service 接口的父类（可选）
         * 用于统一定义通用业务方法
         * 示例：com.baomidou.mybatisplus.extension.service.IService
         * 默认值：null
         */
        private String superServiceClass;

        /**
         * Service 实现类的父类（可选）
         * 用于统一实现通用逻辑
         * 示例：com.baomidou.mybatisplus.extension.service.impl.ServiceImpl
         * 默认值：null
         */
        private String superServiceImplClass;

        /**
         * Controller 的父类（可选）
         * 用于统一返回格式、异常处理等
         * 示例：com.example.base.BaseController
         * 默认值：null
         */
        private String superControllerClass;
    }


    /**
     * 自定义包装类：映射 MyBatis-Plus 的 InjectionConfig
     * 用于指定自定义模板文件路径，覆盖默认模板
     *
     * @author your-name
     * &#064;date  2024-07-05
     */
    @Getter
    @Setter
    public static class InjectionConfig {

        /**
         * 自定义模板文件路径映射（Key 为模板类型，Value 为文件路径）
         * 支持的 Key：
         *   - entity: 实体类模板
         *   - mapper: Mapper 接口模板
         *   - service: Service 接口模板
         *   - serviceImpl: Service 实现类模板
         *   - controller: Controller 模板
         *   - xml: XML 映射文件模板
         *   - other: 其他模板（如 Vue、TS）
         * <p>
         * 示例：
         *   template-path:
         *     entity: /templates/entity.java.ftl
         *     controller: /templates/controller-rest.ftl
         *     other: /templates/vue-list.vue.ftl
         * <p>
         * 注意：路径必须是相对于 classpath 的路径，如：/templates/xxx.ftl
         * 默认值：空 Map（使用 MyBatis-Plus 默认模板）
         */
        private Map<String, String> templatePath;

        private String frontendRootPath = "./project/frontend/src"; // 前端输出根目录

        // 自定义文件模板配置（动态配置）
        private Map<String, CustomFile> customFiles = Map.of(
                // 生成 API 文件
                "api.ts", new CustomFile(
                        "api",
                        "Api.ts",
                        "templates/ts/api.ts.ftl",
                        true
                ),
                // 生成类型定义文件
                "types.ts", new CustomFile(
                        "types",
                        "Type.ts",
                        "templates/ts/types.ts.ftl",
                        true
                ),
                // 生成 Vue 列表页
                "Table.vue", new CustomFile(
                        "views",
                        "Table.vue",
                        "templates/vue/table.vue.ftl",
                        true
                ),
                // 生成 Vue 表单页
                "Form.vue", new CustomFile(
                        "views",
                        "Form.vue",
                        "templates/vue/form.vue.ftl",
                        true
                ),
                // 生成 DTO
                "DTO.java", new CustomFile(
                        "model.dto",
                        "DTO.java",
                        "templates/java/dto.java.ftl",
                        true
                ),
                // 生成 VO
                "VO.java", new CustomFile(
                        "model.vo",
                        "VO.java",
                        "templates/java/vo.java.ftl",
                        true
                ),
                // 生成 BaseEntity（可选）
                "BaseEntity.java", new CustomFile(
                        "model.entity.base",
                        "BaseEntity.java",
                        "templates/java/base-entity.java.ftl",
                        true
                ),
                // 添加其他文件模板（可选）
                "application.yaml", new CustomFile(
                        "model.entity.other",
                        "OtherFile.java",
                        "templates/java/other-file.java.ftl",
                        true
                )
        );

        // 自定义文件配置类
        @Getter
        @Setter
        public static class CustomFile {
            // getter/setter 省略（Lombok 可选，但需手动实现 getter）
            private String packageName; // 生成文件的包名（相对于 packageConfig.parent.moduleName）
            private String fileName; // 生成的文件名（不包含表名）
            private String templatePath; // 模板路径（classpath 下）
            private Boolean enableFileOverride; // 是否覆盖

            public CustomFile(String packageName, String fileName, String templatePath, Boolean enableFileOverride) {
                this.packageName = packageName;
                this.fileName = fileName;
                this.templatePath = templatePath;
                this.enableFileOverride = enableFileOverride;
            }
        }
    }
}
